# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: xguo

import collections
import datetime
from typing import Union

from coin.base.hostname import get_hostname
from coin.base.timestamp import get_timestamp
from coin.exchange.base.kr_rest.rest_client_base import RestUpdate
import coin.proto.exchange_status_pb2 as coin_checker
from xunkemgmt.support.accounting.logic.query_request import \
  get_request_id_from_request
import google.protobuf.json_format as json_format

ExchangeTestRequest = collections.namedtuple(
    'ExchangeTestRequest',
    [
        'account_request',  # type: AccountRequest
        'product',  # type: str
        'gen_test_orders',  # type: Callable
    ])


# TODO(xguo) Following code is useful, put it in some other place.
# convert a timestamp to nano seconds timestamp, no matter the given timestamp
# is measure in seconds, miliseconds, microseconds or nanoseconds.
def convert_to_nano_timestamp(timestamp: Union[int, float]):
  time_units = {'sec': 1, 'millisec': 1e3, 'microsec': 1e6, 'nanosec': 1e9}

  def try_to_convert(ts, unit):
    min_dt = datetime.datetime(year=2010, month=1, day=1)
    max_dt = min_dt + datetime.timedelta(days=20 * 365)
    try:
      dt = datetime.datetime.fromtimestamp(ts / time_units[unit])
      if min_dt <= dt < max_dt:
        return int(ts * int(1e9 / time_units[unit]))
    except (ValueError, OSError):
      pass

  # Possible values contains a valid value and a None.
  possible_values = {try_to_convert(timestamp, unit) for unit in time_units}
  possible_values.remove(None)
  if len(possible_values) != 1:
    raise ValueError('Invalid timestamp %s' % timestamp)
  return list(possible_values)[0]


def convert_to_status_check_request_proto(request: ExchangeTestRequest):
  account_request_id = get_request_id_from_request([request.account_request])[0]
  request_proto = coin_checker.ExchangeStatusRequestProto(
      type=coin_checker.REST_REQUEST,
      exchange_api_id=account_request_id.exchange_api_id,
      account_id=account_request_id.account_id,
      symbol=request.product,
      hostname=get_hostname(),
      query_timestamp=get_timestamp())
  return request_proto


def response_time_in_nano(update: RestUpdate):
  # RestUpdate's time fields are in seconds, convert it to nano seconds.
  req_timestamp = convert_to_nano_timestamp(update.req_timestamp)
  res_timestamp = convert_to_nano_timestamp(update.res_timestamp)
  time_diff = res_timestamp - req_timestamp
  assert time_diff > 0.0, time_diff
  return time_diff
